package jp.ac.waseda.cs.washi.game;

import jp.ac.waseda.cs.washi.game.entity.Chara;
import jp.ac.waseda.cs.washi.game.entity.Map;
import jp.ac.waseda.cs.washi.game.entity.Tile;
import jp.ac.waseda.cs.washi.gameaiarena.api.Point2;

//マップの塗りつぶし判定に関する処理を扱います。
public class MapManager {
	
	//あるマスからの周囲８マスへの差分を表す配列
	private static Point2[] _dp = { new Point2(-1, 0), new Point2(-1, -1),
		new Point2(0, -1), new Point2(1, -1), new Point2(1, 0),
		new Point2(1, 1), new Point2(0, 1), new Point2(-1, 1), };

	//壁であること(null)と、まだオーナーが未定であること(_nullObject)を区別するためのオブジェクト
	private static final Chara _nullObject = new Chara(null, null, null, null, null);

	//指定されたマスを囲っているプレイヤーを返します。マスがどのプレイヤーからも囲われていない場合はnull。
	public static Chara getEnclosureOwner(Map map, Point2 point) {
		return getEnclosureOwner(map, point,
				new boolean[map.getHeight() + 2][map.getWidth() + 2]);
	}	
	
	private static Chara getEnclosureOwner(Map map, Point2 point,
			boolean[][] check) {

		check[point.y][point.x] = true;
		Chara firstOwner;

		// OVERFILL_OPTION が true なら範囲内に敵マスがあっても塗りつぶせる
		if (!Settings.OVERFILL_OPTION) {
			firstOwner = map.getTile(point).getCompleteOwner();
			if (firstOwner == null) {
				firstOwner = _nullObject;
			}
		} else {
			firstOwner = _nullObject;
		}

		//壁に到達した場合は囲まれた状態になりえないのでnullを返す
		if (map.getTile(point).hasWall()) {
			return null;
		}

		//周囲8マスについて探索する
		for (Point2 p : _dp) {
			Point2 around = point.add(p);

			//すでに探索したマスであれば次のマスへ進む
			if (check[around.y][around.x]) {
				continue;
			}

			Tile t = map.getTile(around);
			Chara aroundOwner = t.getCompleteOwner();

			// 灰色タイルのタイルの場合、さらにその周りを探索する
			if (aroundOwner == null) { //このnullは現在のマスにオーナーがいないことを示す
				aroundOwner = getEnclosureOwner(map, around, check);
				if (aroundOwner == null) //このnullは現在のマスが囲み状態にないことを示す
					return null;
			}

			//どちらのオーナーについて探索しているか決定されていない場合は、現在のマスのオーナーを対象に設定する
			if (firstOwner == _nullObject) {
				firstOwner = aroundOwner;
			} else if (firstOwner != aroundOwner) { 
				//現在対象としているオーナーと別のオーナーのマスだった場合はnullを返す
				return null;
			}
		}
		//8マス探索して壁または相手のマスに到達しなかったので、現在対象としているオーナーを返す
		return firstOwner;
	}

	//指定されたタイルをコンプリートしたときに、メテオが発動するかどうかを返します
	public static boolean isInvokableMeteor(Map map, Chara chara, Point2 location) {
		Tile tile = map.getTile(location);
		
		//現在の状態を一時保存しておく
		Chara incompleteOwner = tile.getOwner();
		Chara owner = tile.getCompleteOwner();
		int strength = tile.getStrength();
		
		//指定されたタイルがコンプリートになったときの状態を仮定する
		tile.setIncompleteOwner(chara);
		tile.setOwner(chara);
		tile.setStrength(Settings.COMPLETE_STRENGTH);
		
		//周囲８マスのうち、１つでも囲まれるマスがあるかどうか判定する
		boolean isInvokable = false;
		for(Point2 dp : _dp) {
			Point2 around = location.add(dp);
			Chara c = getEnclosureOwner(map, around);
			if(c != null && c.equals(chara)) {
				isInvokable = true;
				break;
			}
		}
		
		//保存しておいた元の状態に復元する
		tile.setIncompleteOwner(incompleteOwner);
		tile.setOwner(owner);
		tile.setStrength(strength);
		return isInvokable;
	}
	
	//指定されたマスの周囲のマスについて、再帰的に塗りつぶし処理を行う
	public static void invokeMeteor(Map map, Point2 location) {
		for(Point2 dp : _dp) {
			Point2 around = location.add(dp);
			if(map.getTile(around).getCompleteOwner() != null)
				continue;
			Chara chara = getEnclosureOwner(map, around);
			if(chara != null) {
				Tile tile = map.getTile(around);
				tile.setOwner(chara);
				tile.setIncompleteOwner(chara);
				tile.setStrength(Settings.COMPLETE_STRENGTH);
				invokeMeteor(map, around);
			}
		}
	}
}